/**
 *  防止循环引用:使用 WeakMap 来存储已经拷贝的对象，防止因循环引用导致的无限递归
 *  内存管理：WeakMap 中的键是弱引用，这意味着当没有其他引用指向键时，键值对会被垃圾回收。使用普通对象或 Map 作为存储结构的话，如果对象的引用链很长，可能会导致内存无法被及时回收，造成内存泄漏。
 *    是弱引用，内存管理更为高效。在处理大量数据或复杂对象时，性能和内存占用是非常重要的考虑因素。
 *  防止意外引用：使用普通对象作为缓存会导致意外的引用保持，从而使得在不需要的情况下仍然占用内存。使用 WeakMap 则不会有这个问题，确保只在需要时保持引用。
 */
function deepClone(obj, hash = new WeakMap()) {
  // 处理 null值
  if (obj === null) return obj;

  // 处理Date对象的值
  if (obj instanceof Date) return new Date(obj);

  // 处理RegExp对象的值
  if (obj instanceof RegExp) return new RegExp(obj);

  // 处理Set
  if (obj instanceof Set) {
    const cloneSet = new Set();
    hash.set(obj, cloneSet);
    for (const value of obj) {
      cloneSet.add(deepClone(value, hash));
    }
    return cloneSet;
  }

  // 处理Map
  if (obj instanceof Map) {
    const cloneMap = new Map();
    hash.set(obj, cloneMap);
    for (const [key, value] of obj) {
      cloneMap.set(deepClone(key, hash), deepClone(value, hash));
    }
    return cloneMap;
  }

  // 处理数组
  if (Array.isArray(obj)) {
    const cloneArr = [];
    hash.set(obj, cloneArr);
    for (const item of obj) {
      cloneArr.push(deepClone(item, hash));
    }
    return cloneArr;
  }

  // 处理 Symbol 的值
  if (typeof obj === "symbol") {
    return Symbol(obj.description);
  }

  //如果是函数类型是不需要做深拷贝的，函数是用来执行的，一般是不会更改的
  //深拷贝的目的是，改变新对象的任何属性，对原函数没有任何影响
  if (typeof obj === "function") return obj;

  // 处理基本数据类型
  if (typeof obj !== "object") return obj;

  // 有缓存拿到缓存的值
  if (hash.has(obj)) return hash.get(obj);

  // 没缓存创建新对象
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  let cloneObj = new obj.constructor();
  hash.set(obj, cloneObj);

  // 递归拷贝对象的属性（包括普通属性和 Symbol 属性）
  const keys = Reflect.ownKeys(obj); // 获取所有属性，包括 Symbol
  for (const key in keys) {
    cloneObj[keys[key]] = deepClone(obj[keys[key]], hash);
  }

  //Symbol作为key,用for in遍历时看不到值的
  //for in只会遍历普通的key,单独遍历symbol
  const symbols = Object.getOwnPropertySymbols(obj); // 获取所有Symbol属性
  for (let sym of symbols) {
    cloneObj[Symbol(sym.description)] = deepClone(obj[sym], hash);
  }

  return cloneObj;
}

const sym1 = Symbol("sym1");
const sym2 = Symbol("sym2");

const original = {
  a: 1,
  b: { c: 2 },
  [sym1]: "symbol value 1",
  [sym2]: "symbol value 2",
  d: new Date(),
  e: new Set([1, 2, 3]),
  f: new Map([
    [1, "one"],
    [2, "two"],
  ]),
  g: [4, 5, 6],
  h: function () {
    return "I am a function";
  },
};

// 创建一个循环引用
original.self = original;

console.log("original: ", original);

const cloned = deepClone(original);

// 测试
console.log(cloned); // 输出深拷贝的对象
console.log(cloned === original); // false
console.log(cloned[sym1]); // 输出 'symbol value 1'
console.log(cloned[sym2]); // 输出 'symbol value 2'
console.log(cloned.d instanceof Date); // 输出 true
console.log(cloned.e instanceof Set); // 输出 true
console.log(cloned.f instanceof Map); // 输出 true
console.log(cloned.g); // 输出 [4, 5, 6]
console.log(cloned.h()); // 输出 'I am a function'

// 验证循环引用
console.log(cloned.self === cloned); // 输出 true，验证循环引用正确

// -----
function deepClone1(obj, hash = new WeakMap()) {
  if (obj === null) return obj;

  if (obj instanceof RegExp) return new RegExp(obj);

  if (obj instanceof Date) return new Date(obj);

  if (obj instanceof Set) {
    let cloneSet = new Set();
    hash.set(obj, cloneSet);
    for (const value of obj) {
      cloneSet.add(deepClone1(value, hash));
    }
    return cloneSet;
  }

  if (obj instanceof Map) {
    let cloneMap = new Map();
    hash.set(obj, cloneMap);
    for (const [key, value] of obj) {
      cloneMap.set(deepClone(key, hash), deepClone(value, hash));
    }
    return cloneMap;
  }

  if (Array.isArray(obj)) {
    let cloneArr = [];
    hash.set(obj, cloneArr);
    for (const item of obj) {
      cloneArr.push(deepClone1(item, hash));
    }
    return cloneArr;
  }

  if (typeof obj === "symbol") {
    return Symbol(obj.description);
  }

  if (typeof obj === "function") {
    return obj;
  }

  if (typeof obj !== "object") {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  const cloneObj = new obj.constructor();
  hash.set(obj, cloneObj);

  const keys = Reflect.ownKeys(obj);

  for (let index in keys) {
    cloneObj[keys[index]] = deepClone1(obj[keys[index]], hash);
  }

  const symbols = Object.getOwnPropertySymbols(obj);
  for (let sym of symbols) {
    cloneObj[Symbol(sym.description)] = deepClone1(obj[sym], hash);
  }

  return cloneObj;
}

function deepClone2(obj, hash = new WeakMap()) {
  if (obj === null) return obj;

  if (obj instanceof RegExp) return new RegExp(obj);

  if (obj instanceof Date) return new Date(obj);

  if (obj instanceof Set) {
    let cloneSet = new Set();
    hash.set(obj, cloneSet);
    for (value of obj) {
      cloneSet.add(deepClone(obj, hash));
    }
    return cloneSet;
  }

  if (obj instanceof Map) {
    let cloneMap = new Map();
    hash.set(obj, cloneMap);
    for (const [key, value] of obj) {
      cloneMap.set([deepClone(key, hash), deepClone(value, hash)]);
    }
    return cloneMap;
  }

  if (Array.isArray(obj)) {
    let cloneArr = [];
    hash.set(obj, cloneArr);
    obj.forEach((ele) => {
      cloneArr.push(deepClone(ele, hash));
    });
    return cloneArr;
  }

  if (typeof obj === "function") {
    return obj;
  }

  if (typeof obj === "symbol") {
    return Symbol(obj.description);
  }

  if (typeof obj !== "object") {
    return obj;
  }

  if (hash.has(obj)) {
    return hash.get(obj);
  }

  let cloneObj = new obj.constructor();
  hash.set(obj, cloneObj);
  let keys = Reflect.ownKeys(obj);
  for (let i in keys) {
    if (keys.hasOwnProperty(i)) {
      cloneObj[keys[i]] = deepClone(obj[keys[i]]);
    }
  }

  let symbols = Object.getOwnPropertySymbols();
  for (sym of symbols) {
    cloneObj[Symbol(sym.description)] = deepClone(obj[sym], hash);
  }

  return cloneObj;
}
